﻿using JESAI.EventBus.Attributes;
using JESAI.EventBus.Contexts;
using JESAI.EventBus.EventBusStores;
using JESAI.EventBus.Events;
using JESAI.EventBus.ExcutedResults;
using JESAI.EventBus.Executors;
using JESAI.EventBus.Handles;
using JESAI.EventBus.Monitors;
using JESAI.EventBus.PublishManagers;
using JESAI.EventBus.Retrys;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics.Tracing;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Reflection.Metadata;
using System.Runtime.CompilerServices;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using static JESAI.EventBus.Retrys.EventHandleRetry;

namespace JESAI.EventBus.HostBackgroundServices
{
    internal class EventBusHostBackgroundService : BackgroundService
    {
        private readonly ILogger<EventBusHostBackgroundService> _logger;

        private const int GC_COLLECT_INTERVAL_Min_SECONDS = 3;

        private readonly IEventStore _eventStore;
        private IEventBusMonitor? _monitor { get; }

        private readonly IEventPublishManager? _eventPublishManager;

        internal event EventHandler<UnobservedTaskExceptionEventArgs> UnhandleExcepiton;

        private DateTime? PreHandGCCollectTime { get; set; }

        /// <summary>
        /// 事件处理程序集合
        /// </summary>
        private readonly ConcurrentDictionary<EventSubscribeHandle, EventSubscribeHandle> _eventSubscribers = new();

        private readonly ICustomEventHandExecutor? _customEventHandExecutor;

        private IServiceProvider _serviceProvider;



        /// <summary>
        /// 是否启用日志记录
        /// </summary>
        private bool EnabledLogOut { get; }

        public EventBusHostBackgroundService(ILogger<EventBusHostBackgroundService> logger,
            IServiceProvider serviceProvider,
            IEventStore eventStore,
            IEnumerable<IEventSubscriber> eventSubscribers,
            bool enabledLogOut)
        {
            _logger = logger;
            EnabledLogOut = enabledLogOut;
            _eventStore = eventStore;
            _serviceProvider = serviceProvider;
            _customEventHandExecutor = _serviceProvider.GetService<ICustomEventHandExecutor>();
            _eventPublishManager = _serviceProvider.GetService<IEventPublishManager>();
            _monitor = _serviceProvider.GetService<IEventBusMonitor>();
            var bindingAttr = BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly;
            // 逐条获取事件处理程序并进行包装
            foreach (var eventSubscriber in eventSubscribers)
            {
                var type = eventSubscriber.GetType();
                var eventAttributeMethods = type.GetMethods(bindingAttr).Where(u => u.IsDefined(typeof(EventSubscribeAttribute), false));

                foreach (var eventAttributeMethod in eventAttributeMethods)
                {
                    var handle = (Func<EventExecutingContext, Task>)eventAttributeMethod.CreateDelegate(typeof(Func<EventExecutingContext, Task>), eventSubscriber);

                    var eventSubscribeAttributes = eventAttributeMethod.GetCustomAttributes<EventSubscribeAttribute>(false);


                    foreach (var eventSubscribeAttribute in eventSubscribeAttributes)
                    {
                        var subscriber = new EventSubscribeHandle(eventSubscribeAttribute.EventId)
                        {
                            Handle = handle,
                            HandleMethod = eventAttributeMethod,
                            Attribute = eventSubscribeAttribute,
                            Pattern = eventSubscribeAttribute.IsEnabledPattern ? new Regex(eventSubscribeAttribute.EventId, RegexOptions.Singleline) : default,
                            IsEnabledGCCollect = eventSubscribeAttribute.IsEnabledGCCollect,
                            Order = eventSubscribeAttribute.Order
                        };

                        _eventSubscribers.TryAdd(subscriber, subscriber);
                    }
                }

                //根据特性查找：TODO

            }
        }


        /// <summary>
        /// 避免由 CLR 的终结器捕获该异常从而终止应用程序，让所有未觉察异常被觉察
        /// </summary>
        internal event EventHandler<Exception> UnhandleException;

        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            while (!stoppingToken.IsCancellationRequested)
            {
                try
                {
                    await EventBusProcessing(stoppingToken);
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, "An error occurred while processing events");
                }
            }
        }

        private async Task EventBusProcessing(CancellationToken stoppingToken)
        {
            // 从事件存储器中读取一条
            var @event = await _eventStore.Pop(stoppingToken);

            if (@event is SubscribeEvent)
            {
                RefreshEventSubscribers(@event as SubscribeEvent);
                return;
            }

            // 空检查
            if (@event == null || string.IsNullOrWhiteSpace(@event?.EventId))
            {
                Log(LogLevel.Warning, "校验事件ID出错，事件或者事件ID为空!");
                return;
            }

            // 查找事件 Id 匹配的事件处理程序
            var eventHandles = _eventSubscribers.Where(t => t.Key.CanRun(@event.EventId)).OrderByDescending(u => u.Value.Order)
                .Select(u => u.Key)
                .ToList();

            // 空订阅
            if (!eventHandles.Any())
            {
                Log(LogLevel.Warning, $"{@event.EventId}未找到对应的订阅,忽略。");
                return;
            }

            // 检查是否配置只消费一次
            if (!@event.AllowMultiConsum)
            {
                var randomId = RandomNumberGenerator.GetInt32(0, eventHandles.Count);
                eventHandles = [eventHandles.ElementAt(randomId)];
            }

            PublishEvent publishEvent = null;
            if (@event is PublishEvent)
            {
                publishEvent = @event as PublishEvent;
                if (publishEvent.RunTime.HasValue && publishEvent.RunTime <= DateTime.Now)
                {
                    return;
                }
            }
            var taskFactory = new TaskFactory(TaskScheduler.Current);

            // 遍历所有事件处理程序
            Parallel.ForEach(eventHandles, (handle) =>
            {
                // 创建新的线程执行
                taskFactory.StartNew(async () =>
                {
                    InvalidOperationException executionException = default;
                    var eventExecutingContext = new EventExecutingContext(@event, handle.HandleMethod, handle.Attribute);
                    try
                    {
                        if (@event.CancellationToken.IsCancellationRequested)
                        {
                            // 调用执行前监视器
                            if (_monitor != default)
                            {
                                await _monitor.OnExecutCancelAsync(eventExecutingContext);
                            }
                            return;
                        }
                        // 调用执行前监视器
                        if (_monitor != default)
                        {
                            await _monitor.OnExecutBeforeAsync(eventExecutingContext);
                        }
                        if (_customEventHandExecutor == null)
                        {
                            await EventHandleRetry.ExecuteAsync(new EventExcuteHandle(async () =>
                            {
                                if (_monitor != default)
                                {
                                    await _monitor.OnExecutingAsync(eventExecutingContext);
                                }
                                await handle.Handle!(eventExecutingContext);
                            })
                            {
                                RetryCount = handle.Attribute.RetryCount,
                                RetryInterval = handle.Attribute.RetryInterval,
                                RetryIntervals = handle.Attribute.RetryIntervals,
                                ExceptionCallback = (exception) =>
                                {
                                    Log(LogLevel.Error, $"执行消费发生错误 {@event.EventId}.", ex: exception);
                                    return Task.FromResult(false);
                                },
                                ExceptionTypes = handle.Attribute.ExceptionTypes,
                                RetryOutPutAction = (totalRetryCount, currentRetryCount) =>
                                {
                                    Log(LogLevel.Information, $"执行消费发生错误 {@event.EventId}, 重试次数: {totalRetryCount},当前重试次数: {currentRetryCount}.");
                                }
                            });
                        }
                        else
                        {
                            if (_monitor != default)
                            {
                                await _monitor.OnExecutingAsync(eventExecutingContext);
                            }
                            await _customEventHandExecutor.ExecuteAsync(eventExecutingContext, handle.Handle, stoppingToken);
                        }

                        var result = new EventHandleResult(@event, EventHandleResultType.Success);
                        publishEvent?.PublishedCallback(result);
                        _eventPublishManager?.GrobalPublishedCallback(result);
                    }
                    catch (Exception ex)
                    {

                        if (UnhandleExcepiton != default)
                        {
                            var args = new UnobservedTaskExceptionEventArgs(
                                ex as AggregateException ?? new AggregateException(ex));

                            UnhandleExcepiton.Invoke(this, args);
                        }

                        executionException = new InvalidOperationException(string.Format("执行消费发生错误 {0}.", @event.EventId), ex);

                        var result = new EventHandleResult(@event, EventHandleResultType.Success)
                        {
                            Exception = ex
                        };
                        if (_monitor != default)
                        {
                            // 创建执行后上下文
                            var eventHandlerExecutedContext = new EventExecutedContext(@event, handle.HandleMethod, handle.Attribute);
                            await _monitor.OnExecutExceptionAsync(eventHandlerExecutedContext);
                        }
                        _eventPublishManager?.GrobalPublishedCallback(result);

                        publishEvent?.PublishedCallback(result);

                    }
                    finally
                    {
                        // 调用执行后监视器
                        if (_monitor != default)
                        {
                            var eventHandlerExecutedContext = new EventExecutedContext(@event, handle.HandleMethod, handle.Attribute);
                            await _monitor.OnExecutedAsync(eventHandlerExecutedContext);
                        }

                        // 清除垃圾
                        var nowTime = DateTime.UtcNow;
                        if (handle.IsEnabledGCCollect && (PreHandGCCollectTime == null || (nowTime - PreHandGCCollectTime.Value).TotalSeconds > GC_COLLECT_INTERVAL_Min_SECONDS))
                        {
                            PreHandGCCollectTime = nowTime;
                            GC.Collect();
                            GC.WaitForPendingFinalizers();
                        }
                    }
                });
            });
        }

        private void RefreshEventSubscribers(SubscribeEvent subscribeEvent)
        {
            var eventId = subscribeEvent.EventId;

            if (subscribeEvent.Attribute != null && subscribeEvent.Attribute.EventId != eventId)
                throw new InvalidOperationException("确保<eventId>与EventSubscribeAttribute对象的<eventId>属性一致。");

            //订阅
            if (subscribeEvent.SubscribeType == EventSubscribeType.Subscribe)
            {
                var subscriber = new EventSubscribeHandle(eventId)
                {
                    Attribute = subscribeEvent.Attribute,
                    HandleMethod = subscribeEvent.HandleMethod,
                    Handle = subscribeEvent.Handle,
                    Pattern = (bool)subscribeEvent.Attribute?.IsEnabledPattern ? new Regex(eventId, RegexOptions.Singleline) : default,
                    IsEnabledGCCollect = (bool)subscribeEvent.Attribute?.IsEnabledGCCollect,
                    Order = subscribeEvent.Attribute?.Order ?? 0
                };

                // 追加到集合中
                var succeeded = _eventSubscribers.TryAdd(subscriber, subscriber);

                // 输出日志
                if (succeeded)
                {
                    Log(LogLevel.Information, $"事件ID为{eventId}的订阅服务已成功附加。");
                }
            }
            // 取消订阅
            else if (subscribeEvent.SubscribeType == EventSubscribeType.Unsubscribe)
            {
                // 删除所有匹配事件 Id 的处理程序
                foreach (var subscriber in _eventSubscribers.Keys)
                {
                    if (subscriber.EventId != eventId) continue;

                    var succeeded = _eventSubscribers.TryRemove(subscriber, out _);
                    if (!succeeded) continue;

                    // 输出日志
                    Log(LogLevel.Warning, $"事件ID为{eventId}的订阅服务{subscriber.HandleMethod.Name}已被删除。");
                }
            }
        }

        private void Log(LogLevel logLevel, string message, object[] args = default, Exception ex = default)
        {
            // 如果未启用日志记录则直接返回
            if (!EnabledLogOut) return;

            if (logLevel == LogLevel.Error)
            {
                _logger.LogError(ex, message, args);
            }
            else
            {
                _logger.Log(logLevel, message, args);
            }
        }
    }
}
